package nl.hanze.web.t41.http;

import java.io.*;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;

public class HTTPRequest {

	private static final char[] map1 = new char[64];
	static {
		int i = 0;
		for (char c = 'A'; c <= 'Z'; c++)
			map1[i++] = c;
		for (char c = 'a'; c <= 'z'; c++)
			map1[i++] = c;
		for (char c = '0'; c <= '9'; c++)
			map1[i++] = c;
		map1[i++] = '+';
		map1[i++] = '/';
	}
	private static final byte[] map2 = new byte[128];
	static {
		for (int i = 0; i < map2.length; i++)
			map2[i] = -1;
		for (int i = 0; i < 64; i++)
			map2[map1[i]] = (byte) i;
	}

	private String path; // stores the path for the requested file
	// private File webRoot;
	private String[] requestHeader;
	private boolean isPageRequest = false;
//	private boolean hasBody = false;
	private boolean hasCredentials = false;
	private String username = "";
	private String password = "";

	public HTTPRequest(InputStream in, File webRoot) {

		// this.webRoot = webRoot;

		String tmp = " ";
		String input = "";
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		try {
			while (!tmp.equals("")) { // read from the stream
				tmp = br.readLine();
				input += tmp + "\r\n";
				if (tmp.toLowerCase().startsWith("authorization: basic ")) {
					String[] decoded = new String(base64decode(tmp.split(" ")[2].toCharArray())).split(":", 2);
//					System.out.println(new ArrayList<String>(Arrays.asList(decoded)));
					username = decoded[0];
					password = decoded[1];
					hasCredentials = true;
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		requestHeader = input.trim().split("\\s*\n\\s*");

		System.out.println(requestHeader.length);
		System.out.println(new ArrayList<String>(Arrays.asList(requestHeader)));

		String[] requestType = requestHeader[0].split(" ");

		if (requestType[0].equals("GET") && requestType.length > 1) {
			path = requestType[1];
			isPageRequest = true;
		}
	}

	/**
	 * get full path from requested file
	 */
	public String getPath() {
		return path;
	}
	
	public void appendToPath(String filename) {
		path += HTTPServer.dirSep + filename;
	}

	public boolean isPageRequest() {
		return isPageRequest;
	}

	public boolean hasCredentials() {
		return hasCredentials;
	}
	
	public String getUsername() {
		return username;
	}
	
	public String getPassword() {
		return password;
	}

	/**
	 * Decodes a byte array from Base64 format. No blanks or line breaks are
	 * allowed within the Base64 encoded input data.
	 * 
	 * @param in
	 *            A character array containing the Base64 encoded data.
	 * @param iOff
	 *            Offset of the first character in <code>in</code> to be
	 *            processed.
	 * @param iLen
	 *            Number of characters to process in <code>in</code>, starting
	 *            at <code>iOff</code>.
	 * @return An array containing the decoded data bytes.
	 * @throws IllegalArgumentException
	 *             If the input is not valid Base64 encoded data.
	 */
	private static byte[] base64decode(char[] in) {
		int iOff = 0;
		int iLen = in.length;
		if (iLen % 4 != 0)
			throw new IllegalArgumentException(
					"Length of Base64 encoded input string is not a multiple of 4.");
		while (iLen > 0 && in[iOff + iLen - 1] == '=')
			iLen--;
		int oLen = (iLen * 3) / 4;
		byte[] out = new byte[oLen];
		int ip = iOff;
		int iEnd = iOff + iLen;
		int op = 0;
		while (ip < iEnd) {
			int i0 = in[ip++];
			int i1 = in[ip++];
			int i2 = ip < iEnd ? in[ip++] : 'A';
			int i3 = ip < iEnd ? in[ip++] : 'A';
			if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
				throw new IllegalArgumentException(
						"Illegal character in Base64 encoded data.");
			int b0 = map2[i0];
			int b1 = map2[i1];
			int b2 = map2[i2];
			int b3 = map2[i3];
			if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
				throw new IllegalArgumentException(
						"Illegal character in Base64 encoded data.");
			int o0 = (b0 << 2) | (b1 >>> 4);
			int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2);
			int o2 = ((b2 & 3) << 6) | b3;
			out[op++] = (byte) o0;
			if (op < oLen)
				out[op++] = (byte) o1;
			if (op < oLen)
				out[op++] = (byte) o2;
		}
		return out;
	}
}
